{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "6HTRLzyWW5Q1"
   },
   "source": [
    "##### Copyright 2020 The Cirq Developers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "oOggJBe6W5nF"
   },
   "outputs": [],
   "source": [
    "# @title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "# https://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "9UinLMKSWRkA"
   },
   "source": [
    "# Quantum circuits on Pasqal devices"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "8jnX4ti8W6Lg"
   },
   "source": [
    "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://quantumai.google/cirq/hardware/pasqal/getting_started\"><img src=\"https://quantumai.google/site-assets/images/buttons/quantumai_logo_1x.png\" />View on QuantumAI</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/quantumlib/Cirq/blob/main/docs/hardware/pasqal/getting_started.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/colab_logo_1x.png\" />Run in Google Colab</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://github.com/quantumlib/Cirq/blob/main/docs/hardware/pasqal/getting_started.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/github_logo_1x.png\" />View source on GitHub</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a href=\"https://storage.googleapis.com/tensorflow_docs/Cirq/docs/hardware/pasqal/getting_started.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/download_icon_1x.png\" />Download notebook</a>\n",
    "  </td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "bd9529db1c0b"
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    import cirq\n",
    "except ImportError:\n",
    "    print(\"installing cirq...\")\n",
    "    !pip install --quiet cirq\n",
    "    print(\"installed cirq.\")\n",
    "    import cirq\n",
    "import cirq_pasqal\n",
    "from cirq_pasqal import ThreeDQubit, TwoDQubit, PasqalVirtualDevice, PasqalNoiseModel"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "785bc8599470"
   },
   "source": [
    "<img src=\"../../images/pasqal/Cirq_pasqal.png\" width=\"700\"/>\n",
    "\n",
    "In this notebook, we show how to program a quantum circuit for Pasqal using cirq. The first step is to import cirq, and Pasqal custom classes. We use ``PasqalVirtualDevice`` to showcase how Cirq enforces Pasqal's devices' restrictions throughout the process."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2308f81b64c4"
   },
   "source": [
    "## Create an array of qubits on a lattice\n",
    "\n",
    "The QPU of Pasqal is made of neutral atoms controlled by lasers. Individual atoms are trapped at well-defined positions in 1, 2 or even 3D, as shown on the following plot ( [Nature 561, 79 (2018)](https://www.nature.com/articles/s41586-018-0450-2)).\n",
    "\n",
    "<img src=\"../../images/pasqal/eiffel_tower.png\" width=\"500\"/>\n",
    "\n",
    "We created a custom class in Cirq, ThreeDQubit, that corresponds to a qubit placed in 3D space. Let us start by creating a register comprising $36=6\\times6$ qubits in 2D, regularly arranged on a square lattice. It corresponds to the following configuration (image taken from [Nature 561, 79 (2018)](https://www.nature.com/articles/s41586-018-0450-2))\n",
    "\n",
    "<img src=\"../../images/pasqal/grid_atoms.png\" width=\"300\"/>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "74507c7b7017"
   },
   "outputs": [],
   "source": [
    "width = 6\n",
    "height = 6\n",
    "depth = 1\n",
    "# Creating p_qubits, a list of ThreeDQubits.\n",
    "p_qubits = [\n",
    "    ThreeDQubit(row, col, layer)\n",
    "    for row in range(width)\n",
    "    for col in range(height)\n",
    "    for layer in range(depth)\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0c70e0cd44b6"
   },
   "source": [
    "Notice how we are fixing `depth=1` to keep all qubits in the $z=0$ plane. The same can also be achieved by using the `TwoDQubit` class, which is nothing more than a `ThreeDQubit` confined to the `z=0` plane. We can create the same $6 x 6$ square array, this time using one of the built-in methods:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "YPyeqF3EReB2"
   },
   "outputs": [],
   "source": [
    "p_qubits = TwoDQubit.square(6)  # 6x6 square array of TwoDQubits"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0b0039da850c"
   },
   "source": [
    "## Create a PasqalDevice\n",
    "\n",
    "Multi-qubit gates can be applied between qubits in the device, provided that the distance between them is smaller than the so-called Rydberg blocade radius (or control radius), that can be passed as a parameter of the device (in units of the lattice size). Here, we instantiate a `PasqalVirtualDevice` with 36 qubits and a control radius of $2.1$. \n",
    "\n",
    "This `PasqalVirtualDevice` can be used to validate the operations within a `Circuit`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "c9f65c79ae9e"
   },
   "outputs": [],
   "source": [
    "# Initialize and create a circuit\n",
    "initial_circuit = cirq.Circuit()\n",
    "initial_circuit.append(cirq.CZ(p_qubits[0], p_qubits[1]))\n",
    "initial_circuit.append(cirq.Z(p_qubits[0]))\n",
    "initial_circuit.append(cirq.CX(p_qubits[0], p_qubits[2]))\n",
    "\n",
    "# Create a Pasqal device with a control radius of 2.1 (in units of the lattice spacing)\n",
    "p_device = PasqalVirtualDevice(control_radius=2.1, qubits=p_qubits)\n",
    "\n",
    "# Validate the circuit using the device\n",
    "try:\n",
    "    p_device.validate_circuit(initial_circuit)\n",
    "except ValueError as e:\n",
    "    # Uh oh!  This circuit does not pass validation.\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "aa25f2e644ce"
   },
   "source": [
    "Notice that the `CX` gate, also called a `CNOT` gate is not valid on this device.  We can use a `PasqalGateset` object with the built-in cirq method `cirq.optimize_for_target_gateset` to convert it.  This will convert the logical gates into native hardware gates.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "1JbgjTVzZg6u"
   },
   "outputs": [],
   "source": [
    "pasqal_gateset = cirq_pasqal.PasqalGateset(include_additional_controlled_ops=False)\n",
    "pasqal_circuit = cirq.optimize_for_target_gateset(initial_circuit, gateset=pasqal_gateset)\n",
    "\n",
    "# TODO(https://github.com/quantumlib/Cirq/issues/6655) - remove after fixup\n",
    "pasqal_circuit = cirq.Circuit(pasqal_circuit.all_operations(), strategy=cirq.InsertStrategy.NEW)\n",
    "\n",
    "print(pasqal_circuit)\n",
    "\n",
    "# Now the circuit validates correctly!\n",
    "p_device.validate_circuit(pasqal_circuit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "DkHu3gfNpe2J"
   },
   "source": [
    "When inserting into circuits designed for Pasqal devices, be sure to insert each operation into its own `Moment` (for example, using `cirq.InsertStrategy.NEW`).  You may need to do this in a separate step, as in the below example that converts two simultaneous Hadamard gates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "YtXUHkTfpraF"
   },
   "outputs": [],
   "source": [
    "initial_circuit = cirq.Circuit()\n",
    "initial_circuit.append(cirq.H(p_qubits[0]))\n",
    "initial_circuit.append(cirq.H(p_qubits[1]))\n",
    "print('Initial Circuit, does not pass validation:')\n",
    "print(initial_circuit)\n",
    "try:\n",
    "    p_device.validate_circuit(initial_circuit)\n",
    "except ValueError as e:\n",
    "    # Uh oh!  This circuit does not pass validation.\n",
    "    print(e)\n",
    "\n",
    "pasqal_circuit = cirq.Circuit(initial_circuit.all_operations(), strategy=cirq.InsertStrategy.NEW)\n",
    "print('')\n",
    "print('Converted Circuit, passes validation:')\n",
    "print(pasqal_circuit)\n",
    "\n",
    "# Now the circuit validates correctly!\n",
    "p_device.validate_circuit(pasqal_circuit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "612100a9e1a0"
   },
   "source": [
    "When the distance between the two qubits involved in the gate is greater than the control radius, as shown for example in the following plot, Cirq will raise an error.\n",
    "\n",
    "<img src=\"../../images/pasqal/r_radius.001.png\" width=\"300\"/><br>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "25d50548ab09"
   },
   "outputs": [],
   "source": [
    "# It is impossible to add a multi-qubit gate if the qubits involved are too far away\n",
    "try:\n",
    "    p_device.validate_operation(cirq.CZ(p_qubits[0], p_qubits[-1]))\n",
    "except ValueError as msg:\n",
    "    print(\"ERROR:\", msg)\n",
    "else:\n",
    "    print(\"Failed to raise the expected ValueError.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "bd01f8dc88c9"
   },
   "source": [
    "## Send a circuit to a remote Pasqal machine\n",
    "\n",
    "Using the Circuit class of Cirq, one can then build a circuit that will be implemented with one of Pasqal's devices as a backend.\n",
    "\n",
    "The real QPU is not plugged in yet as a backend. If you send us your circuit, we currently use a classical emulator as a backend to determine the result of your simulation. As emulating a quantum circuit with a large number of qubits is computationnaly intensive, here we choose to realize a circuit on a smaller register.\n",
    "\n",
    "More precisely, we will implement Grover's algorithm to search for the state $|10\\rangle$, which corresponds to the circuit:\n",
    "\n",
    "<img src=\"../../images/pasqal/Grover_circuit.png\" width=\"750\"/><br>\n",
    "\n",
    "Bear in mind that this is a naïve implementation that can be substantially optimized, particularly in the oracle and the usage of an ancilla, but that is beyond the scope of this tutorial.\n",
    "\n",
    "Here is the Cirq implementation of the circuit using generators:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "494c8052fa12"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "# Place qubits at vertices of an equilateral triangle\n",
    "qs = [TwoDQubit(0, 0), TwoDQubit(1, 0), TwoDQubit(0.5, np.sqrt(3) / 2)]\n",
    "q_meas = [TwoDQubit(1, 0), TwoDQubit(0, 0)]\n",
    "\n",
    "\n",
    "def state_preparation():\n",
    "    for q in qs[:-1]:\n",
    "        yield cirq.H(q)\n",
    "    yield cirq.X(qs[-1])\n",
    "    yield cirq.H(qs[-1])\n",
    "\n",
    "\n",
    "def oracle():\n",
    "    # Signals the state 10\n",
    "    yield cirq.X(qs[0])\n",
    "    yield cirq.CCX(*qs)\n",
    "    yield cirq.X(qs[0])\n",
    "\n",
    "\n",
    "def grover_operator():\n",
    "    for q in qs[:-1]:\n",
    "        yield cirq.H(q)\n",
    "        yield cirq.X(q)\n",
    "\n",
    "    yield cirq.CZ(*qs[:-1])\n",
    "\n",
    "    for q in qs[:-1]:\n",
    "        yield cirq.X(q)\n",
    "        yield cirq.H(q)\n",
    "\n",
    "\n",
    "def generate_grover():\n",
    "    yield state_preparation()\n",
    "    yield oracle()\n",
    "    yield grover_operator()\n",
    "\n",
    "\n",
    "def generate_pasqal_grover():\n",
    "    pasqal_gateset = cirq_pasqal.PasqalGateset(include_additional_controlled_ops=False)\n",
    "    grover_cicruit = cirq.optimize_for_target_gateset(\n",
    "        cirq.Circuit(generate_grover()), gateset=pasqal_gateset\n",
    "    )\n",
    "    return cirq.Circuit(grover_cicruit.all_operations(), strategy=cirq.InsertStrategy.NEW)\n",
    "\n",
    "\n",
    "device = PasqalVirtualDevice(control_radius=1.1, qubits=qs)\n",
    "grover_circuit = generate_pasqal_grover()\n",
    "device.validate_circuit(grover_circuit)\n",
    "grover_circuit.append(cirq.measure(*q_meas, key='x'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ea966b4466aa"
   },
   "source": [
    "Now, to execute the circuit on one of our servers, one has to create a sampler that will send the circuit to the remote host address. One currently needs a token to do this. Please contact us to get one !"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "8214ee49f634"
   },
   "outputs": [],
   "source": [
    "# To execute on one of Pasqal's servers, get an access token and uncomment these lines\n",
    "# sampler = cirq_pasqal.PasqalSampler(remote_host='http://34.98.71.118/v0/pasqal',\n",
    "#                                     access_token='')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "70151da8a002"
   },
   "source": [
    "Alternatively, small circuits can also be simulated locally using Cirq's `Simulator`, which we will do here. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "8918f73e67c1"
   },
   "outputs": [],
   "source": [
    "sampler = cirq.Simulator()\n",
    "data_raw = sampler.run(grover_circuit, repetitions=200)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "54df16cee323"
   },
   "source": [
    "Next, we can plot the results obtained."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "ab8f82fc985a"
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Create dictionary of results\n",
    "data0 = data_raw.data.to_dict()\n",
    "vec = [j for j in range(4)]\n",
    "res = np.zeros(len(vec))\n",
    "for jj in range(200):\n",
    "    number = data0['x'][jj]\n",
    "    res[number] += 1\n",
    "res = res / 200.0\n",
    "\n",
    "# Display plot of states\n",
    "fig = plt.figure()\n",
    "ax = fig.add_axes([0.16, 0.16, 0.78, 0.78])\n",
    "ax.plot(vec, res, 'o')\n",
    "ax.set_xticks(vec)\n",
    "ax.set_xticklabels([r'$|00\\rangle$', r'$|01\\rangle$', r'$|10\\rangle$', r'$|11\\rangle$'])\n",
    "plt.xlabel(r'$j$', fontsize=20)\n",
    "plt.ylabel(r'$p$', fontsize=20)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2243a99d0e8f"
   },
   "source": [
    "In this particular case, it takes a single Grover iteration to find the correct result ($|10\\rangle$) with 100% probability on a perfect device."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "389112dcf80a"
   },
   "source": [
    "## Incorporate the effect of noise during a classical emulation of the circuit\n",
    "\n",
    "Current NISQ processors are imperfect and prone to errors. We incorporated in Cirq a typical noise model for Pasqal devices, that can be used when one emulates the behavior of the device with a classical computer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "675a72331dca"
   },
   "outputs": [],
   "source": [
    "# Use the custom noise model of Pasqal devices\n",
    "noise_model = PasqalNoiseModel(device)\n",
    "grover_circuit = generate_pasqal_grover()\n",
    "grover_circuit = grover_circuit.with_noise(noise_model)\n",
    "grover_circuit.append(cirq.measure(*q_meas, key='x'))\n",
    "\n",
    "# Run a simulation locally locally\n",
    "data_raw = sampler.run(grover_circuit, repetitions=200)\n",
    "\n",
    "# Plot the results\n",
    "data0 = data_raw.data.to_dict()\n",
    "vec = [j for j in range(4)]\n",
    "res = np.zeros(len(vec))\n",
    "for jj in range(200):\n",
    "    number = data0['x'][jj]\n",
    "    res[number] += 1\n",
    "res = res / 200.0\n",
    "\n",
    "fig = plt.figure()\n",
    "ax = fig.add_axes([0.16, 0.16, 0.78, 0.78])\n",
    "ax.plot(vec, res, 'o')\n",
    "ax.set_xticks(vec)\n",
    "ax.set_xticklabels([r'$|00\\rangle$', r'$|01\\rangle$', r'$|10\\rangle$', r'$|11\\rangle$'])\n",
    "plt.xlabel(r'$j$', fontsize=20)\n",
    "plt.ylabel(r'$p$', fontsize=20)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "c2e5c717a4b6"
   },
   "source": [
    "One still finds a rather large probability to measure $|10\\rangle$, but we note the appearance of other measurement outcomes, due to the presence of noise."
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "getting_started.ipynb",
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
